package ch.usi.inf.pf2.alu;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import ch.usi.inf.pf2.Sheet;
import ch.usi.inf.pf2.alu.nodes.WrongNumberOfArgumentsException;
import ch.usi.inf.pf2.analyzer.UnrecognizedTokenException;
import ch.usi.inf.pf2.cell.Cell;
import ch.usi.inf.pf2.cell.Data;
import ch.usi.inf.pf2.cell.Formula;
import ch.usi.inf.pf2.cell.Value;



/**
 * @author Thomas Mantegazzi,Yotam Sharon
 * @date 16 april 2010
 */


/**
 * ALU class is the arithmetic logic unit, where all calculations will be computed.
 */

public class ALU {

	private Sheet sheet;
	private Parser parser;

	/**
	 * Constrictor for the alu
	 * @param sheet the sheet where the alu have to work
	 */
	public ALU(Sheet sheet) {
		this.sheet = sheet;
		parser = new Parser(sheet);
	}

	/**
	 * Given a formula as a string it compute it
	 * @param formula The String (formula) to be computed
	 * @return The result of the computation as a string
	 */
	private Value computeString(String formula, String cellReference) throws UnrecognizedTokenException,ParenthesisException, UnrecognizedIdentifierException, SyntaxException,WrongNumberOfArgumentsException, CircularReferenceException, MissingParenthesisException, ReferenceToFormulaWithErrorException{
		Value value = (Value)parser.parseFormula(formula, cellReference).evaluate();
		return value;
	}

	/**
	 * Compute the give formula and put the result in its actualValue field
	 * @param formula The Formula that is about to be computed
	 */
	private void computeFormula(Formula formula, String cellReference) {
		String expression = formula.getInstance();
		try{
			Value result = computeString(expression, cellReference);
			if (result.isZeroNode()) {
				formula.setError("Missing values in references");
			} else {
				formula.setNoErrors();
			}
			formula.setActualValue(result.getInstance());
			formula.setComputed(true);
		} catch (UnrecognizedTokenException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (ParenthesisException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (UnrecognizedIdentifierException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (SyntaxException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (WrongNumberOfArgumentsException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (CircularReferenceException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (MissingParenthesisException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (ReferenceToFormulaWithErrorException ex) {
			formula.setError(ex.getMessage());
			String stringOfFormula = formula.getInstance();
			System.out.println(stringOfFormula);
			formula.setActualValue(stringOfFormula);
			formula.setComputed(true);
		} catch (NullPointerException npe) {
			System.out.println("System Crash");			
		}
	}

	/**
	 * Compute a HashSet Of Formulas
	 * @param formulas HashSet of Formulas to be computed
	 */
	public void computeHashSetOfFormulas(HashMap<Cell, String> cells) {
		ArrayList<Cell> cellsToBeRemoved = new ArrayList<Cell>();
		for(Cell c : cells.keySet()) {
			Data d = c.getContent();
			if (d instanceof Formula) {
				computeFormula((Formula) d, cells.get(c));
			} else {
				cellsToBeRemoved.add(c);
			}
		}
		for (Cell c : cellsToBeRemoved) {
			cells.remove(c);
		}
		for (Cell c : cells.keySet()) {
			Data d = c.getContent();
			((Formula) d).setComputed(false);
		}
	}

	/**
	 * Goes through the whole sheet when it find a Formula it compute it
	 *//*
	public void computeSheet() {
		Cell[][] grid = sheet.getGrid();
		for(int i = 0; i< grid.length; i++) {
			for(int j = 0; j< grid[i].length; j++) {
				if(grid[i][j].getContent() instanceof Formula) {
					computeFormula((Formula)grid[i][j].getContent());
				}
			}
		}
	}*/

}
